package com.gf.framework.web.excel;

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.gf.framework.common.utils.hashhash.HashHashMap;
import com.gf.framework.common.utils.SpringContextUtil;
import com.gf.framework.web.annotation.ExportExcel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;

/**
 * Excel导出AOP
 *
 * @author
 * @date 2022-01
 */
@Aspect
@Component
@Slf4j
@Order(1)
public class ExportExcelAspect {
    private static final String IMPORT_KEYWORDS = "export";
    /**
     * 触发分批导出阈值
     */
    private static final Integer PART_EXPORT_NUM = 5000;

    @Around("@annotation(com.gf.framework.web.annotation.ExportExcel)")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        ServletRequestAttributes attributes = (ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = Objects.requireNonNull(attributes.getResponse());
        if (!isExportRequest(request)) {
            return point.proceed();
        }
        Method method = ((MethodSignature) point.getSignature()).getMethod();

        // 获取注解参数值，导出excel
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        ExportExcel exportExcel = methodSignature.getMethod().getAnnotation(ExportExcel.class);
        Object result = point.proceed();
        Type voType = ((ParameterizedType) (method.getGenericReturnType())).getActualTypeArguments()[0];
        Type rawType = ((ParameterizedType) method.getGenericReturnType()).getRawType();

        IExportExcel iExportExcel = SpringContextUtil.getBean(exportExcel.exportClass());
        iExportExcel.export(request, response, rawType, voType, result, exportExcel);
        return null;
    }

    /**
     * 通过请求中是否含有"export=true"来判断是否为导出请求
     */
    private boolean isExportRequest(HttpServletRequest request) {
        return Boolean.TRUE.toString().equals(request.getParameter(IMPORT_KEYWORDS));
    }

    /**
     * 自适应宽度策略
     * 根据每列内容宽度自适应调整，宽度最小不低于10，最大不超过25
     */
    public static class AdaptiveWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {

        /**
         * 存储sheet索引：列索引：宽度
         */
        private final HashHashMap<Integer, Integer, Integer> cache = new HashHashMap<>();

        @Override
        protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
            boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
            if (!needSetWidth) {
                return;
            }
            // 计算列宽
            Integer columnWidth = getColumnWidth(cellDataList, cell, isHead);
            if (columnWidth < 0) {
                return;
            }
            // 最大不超过25，最小不小于10
            columnWidth = Math.min(columnWidth, 25);
            columnWidth = Math.max(columnWidth, 10);

            Integer maxColumnWidth = cache.get(writeSheetHolder.getSheetNo(), cell.getColumnIndex());
            if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                cache.put(writeSheetHolder.getSheetNo(), cell.getColumnIndex(), columnWidth);
                Sheet sheet = writeSheetHolder.getSheet();
                sheet.setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
            }

        }

        /**
         * 计算宽度
         *
         * @param cellDataList
         * @param cell
         * @param isHead
         * @return
         */
        @SuppressWarnings("all")
        private Integer getColumnWidth(List<CellData> cellDataList, Cell cell, Boolean isHead) {
            if (isHead) {
                // 列头宽度打个折，当这列数据的宽度都很小时，列头文字会换行以节省空间
                return stringWidth(cell.getStringCellValue()) * 8 / 10;
            }
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            }
            switch (type) {
                case STRING:
                    return stringWidth(cellData.getStringValue());
                case BOOLEAN:
                    return cellData.getBooleanValue().toString().getBytes().length;
                case NUMBER:
                    return cellData.getNumberValue().toString().getBytes().length;
                default:
                    return -1;
            }

        }

        /**
         * 字符串宽度（中文宽度x2）
         */
        private int stringWidth(String str) {
            int chineseLength = (str.getBytes().length - str.length()) / 2;
            int otherLength = str.length() - chineseLength;
            return chineseLength * 2 + otherLength + 1;
        }
    }
}

